package Both;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;

import Serveur.RechercheTransport;

/**
 * Class descriptif d'un trajet
 * 
 * @author user
 * 
 */
public class Trajet implements Serializable {

    private Ville villeDepart;
    private Ville villeArrive;

    private GregorianCalendar dateDepart;
    private GregorianCalendar dateArrive;

    private int placeOccuper;

    private Transport transport;

    private int distance;

    /**
     * Constructeur normal (pour le XML)
     */
    public Trajet() {
    }

    /**
     * Constructeur normal
     * 
     * @param villeDepart
     * @param villeArrive
     * @param datedepart
     * @param datearrive
     * @param transport
     */
    public Trajet(Ville villeDepart, Ville villeArrive,
            GregorianCalendar datedepart, GregorianCalendar datearrive,
            Transport transport, double distance) {
        super();
        this.villeDepart = villeDepart;
        this.villeArrive = villeArrive;
        this.dateDepart = datedepart;
        this.dateArrive = datearrive;
        this.transport = transport;
        this.placeOccuper = 0;
        this.distance = (int) distance;
    }

    /**
     * retourne le nombre de places occupées
     * 
     * @return int le nombre de place occupé
     */
    public int getPlaceOccuper() {
        return placeOccuper;
    }

    /**
     * permet de changer le nombre de place occupées
     * 
     * @param placeOccuper
     */

    public void setPlaceOccuper(int placeOccuper) {
        this.placeOccuper = placeOccuper;
    }

    /**
     * retourne la date de depart
     * 
     * @return GregorianCalendar
     */

    public GregorianCalendar getDateDepart() {
        return dateDepart;
    }

    /**
     * change la date de depart
     * 
     * @param date
     */

    public void setDateDepart(GregorianCalendar date) {
        this.dateDepart = date;
    }

    /**
     * retourne la date d'arrivé
     * 
     * @return dateArrive
     */

    public GregorianCalendar getDateArrive() {
        return dateArrive;
    }

    /**
     * change la date d'arrivée
     * 
     * @param date
     */

    public void setDateArrive(GregorianCalendar date) {
        this.dateArrive = date;
    }

    @Override
    public String toString() {
        String retour;
        retour = "\nVille de depart : " + villeDepart;
        retour += "\nVille d'arrive : " + villeArrive;
        retour += "\nDistance : " + distance + "km";
        retour += "\nNb place occupe : " + placeOccuper;
        retour += "\nDate arrivé : " + getFormatedDate(dateArrive.getTime());
        retour += "\nDate depart : " + getFormatedDate(dateDepart.getTime());
        retour += "\nTranport : " + transport.toString();
        retour += "\n";

        return retour;
    }

    /**
     * Retourne la date formatee en String
     * 
     * @param Date
     *            d
     * @return String
     */
    public String getFormatedDate(Date d) {

        DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy 'à' HH:mm");
        return dateFormat.format(d);
    }

    /**
     * retourne la Ville de depart
     * 
     * @return Ville de depart
     */

    public Ville getVilleDepart() {
        return villeDepart;
    }

    /**
     * change la ville de depart
     * 
     * @param ville
     */

    public void setVilleDepart(Ville ville) {
        this.villeDepart = ville;
    }

    /**
     * retourne la ville d'arrivée
     * 
     * @return ville d'arrivée
     */
    public Ville getVilleArrive() {
        return villeArrive;
    }

    /**
     * change la Ville d'arrivée
     * 
     * @param ville
     */

    public void setVilleArrive(Ville ville) {
        this.villeArrive = ville;
    }

    /**
     *pertmet d'afficher les resultats par la methode toString
     * 
     */

    public void print() {
        System.out.println(toString());
    }

    @Override
    protected Object clone() {
        // TODO Auto-generated method stub
        Trajet t = new Trajet(this.villeDepart, this.villeArrive,
                this.dateDepart, this.dateArrive, this.transport, this.distance);
        return t;
    }

    /**
     * retourne le transport utilisé
     * 
     * @return Transport utlilisé
     */
    public Transport getTransport() {
        return transport;
    }

    /**
     * change la transport utilisé
     * 
     * @param t
     */

    public void setTransport(Transport t) {
        this.transport = t;
    }

    /**
     * Verifier si le nombre de place est assez importante pour reserver un
     * certain nombre de personne
     * 
     * @param nbPlace
     * @return
     */
    public boolean peutReserver(int nbPlace) {
        // Verifier la validite des parametres
        //
        if (nbPlace <= 0)
            return false;

        // Reserver, si c'est possible, une place sur ce trajet
        //
        if ((placeOccuper + nbPlace) <= transport.getCapacite()) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Reserver le bombre de palce demand�, il faut augmenter le nombre de place
     * occup�.
     * 
     * @param nbplace
     * @return
     */
    public boolean reserver(int nbplace) {
        if (peutReserver(nbplace)) {
            placeOccuper += nbplace;
            return true;
        } else {
            return false;
        }
    }

    /**
     * Enlever le nombre de passag� reserv�
     * 
     * @param nb
     * @return
     */
    public boolean annulerReservation(int nb) {
        // Verifier la validite du parametre
        //
        if (nb <= 0) {
            return false;
        }

        // S'assurer que le nombre de places a liberer ne soit pas trop grand
        if ((placeOccuper - nb) < 0) {
            return false;
        }

        // Liberer les palces
        //
        placeOccuper = placeOccuper - nb;
        return true;
    }

    /**
     * Recuperer le nombre de place dispo
     * 
     * @return the nomber of free place
     */
    public int getNbPlaceDispo() {
        int capacite = transport.getCapacite();
        return capacite - placeOccuper;
    }

    /**
     * Recherche de trajet par ville, par date et par transport et selon les
     * places disponibles
     * 
     * @param ListeTrajetsarg
     * @param villeDepart
     * @param villeArrive
     * @param dateDepart
     * @param dateArrive
     * @param t
     * @return
     */
    public static ArrayList<Trajet> searchTrajet(
            ArrayList<Trajet> ListeTrajetsarg, Ville villeDepart,
            Ville villeArrive, GregorianCalendar dateDepart,
            GregorianCalendar dateArrive, Transport t, int nbPersonne) {

        // Verifier que la liste des trajets n'est pas nulle
        if (ListeTrajetsarg == null) {
            return null;
        }

        ArrayList<Trajet> ListeTrajetRetour = (ArrayList<Trajet>) ListeTrajetsarg
                .clone();

        // Si la ville de depart n'est pas null
        if (villeDepart != null) {
            Iterator<Trajet> it = ListeTrajetsarg.iterator();
            while (it.hasNext()) {
                Trajet current = it.next();
                if (!current.getVilleDepart().equals(villeDepart)) {
                    ListeTrajetRetour.remove(current);
                }
            }
        }

        // Si la ville d'arrivee n'est pas null
        if (villeArrive != null) {
            Iterator<Trajet> it = ListeTrajetsarg.iterator();
            while (it.hasNext()) {
                Trajet current = it.next();
                if (!current.getVilleArrive().equals(villeArrive)) {
                    ListeTrajetRetour.remove(current);
                }
            }
        }

        // Si la date de depart n'est pas null
        if (dateDepart != null) {
            Iterator<Trajet> it = ListeTrajetsarg.iterator();
            while (it.hasNext()) {
                Trajet current = it.next();
                if (!current.getDateDepart().equals(dateDepart)) {
                    ListeTrajetRetour.remove(current);
                }
            }
        }

        // Si la date d'arrivee n'est pas null
        if (dateArrive != null) {
            Iterator<Trajet> it = ListeTrajetsarg.iterator();
            while (it.hasNext()) {
                Trajet current = it.next();
                if (!current.getDateArrive().equals(dateArrive)) {
                    ListeTrajetRetour.remove(current);
                }
            }
        }

        // Si le transport n'est pas null
        if (t != null) {
            Iterator<Trajet> it = ListeTrajetsarg.iterator();
            while (it.hasNext()) {
                Trajet current = it.next();
                if (!current.getTransport().equals(t)) {
                    ListeTrajetRetour.remove(current);
                }
            }
        }

        if (nbPersonne > 0) {
            Iterator<Trajet> it = ListeTrajetsarg.iterator();
            while (it.hasNext()) {
                Trajet current = it.next();
                if (!current.peutReserver(nbPersonne)) {
                    // Supprimer les trajets dans lesquels il n'y a plus de
                    // place diponible.
                    ListeTrajetRetour.remove(current);
                }
            }
        }

        // Rechercher dans les trajets
        if (ListeTrajetsarg.size() == ListeTrajetRetour.size()
                && villeArrive != null && villeDepart != null) {
            ArrayList<Trajet> ListeMod = (ArrayList<Trajet>) ListeTrajetsarg
                    .clone();
            RechercheTransport rech;
            try {
                rech = new RechercheTransport(villeDepart, villeArrive,
                        ListeMod);
            } catch (IllegalArgumentException e) {
                return null;
            }
            if (rech.cheminPossible()) {
                return rech.Chemin();
            }
        }

        return ListeTrajetRetour;
    }

    /**
     * Recuperer le temps necessaire pour effectuer le trajet
     * 
     * @return the time for travel
     */
    public int getTime() {
        int h1 = ((dateArrive.getGregorianChange().getHours()) * 60)
                + ((dateArrive.getGregorianChange().getMinutes()));
        int h2 = (dateDepart.getGregorianChange().getHours()) * 60
                + ((dateDepart.getGregorianChange().getMinutes()));
        return h1 - h2;
    }

    /**
     * Recuperer en int la date d'arrivé le trajet
     * 
     * @return the time for travel
     */
    public int getTimeDarrive() {
        int h = ((dateArrive.getGregorianChange().getHours()) * 60)
                + ((dateArrive.getGregorianChange().getMinutes()));

        return h;
    }

    /**
     * Recuperer en int la date de depart du trajet
     * 
     * @return the time for travel
     */
    public int getTimeDepart() {

        int h = (dateDepart.getGregorianChange().getHours()) * 60
                + ((dateDepart.getGregorianChange().getMinutes()));
        return h;
    }

    /**
     * Redefinition de la methode equals
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }

        Trajet t = (Trajet) obj;
        if (!t.getDateArrive().equals(this.getDateArrive())) {
            return false;
        }
        if (!t.getDateDepart().equals(this.getDateDepart())) {
            return false;
        }
        if (!t.getTransport().equals(this.getTransport())) {
            return false;
        }
        if (!t.getVilleArrive().equals(this.getVilleArrive())) {
            return false;
        }
        if (!t.getVilleDepart().equals(this.getVilleDepart())) {
            return false;
        }
        if (Double.compare(t.getDistance(), this.getDistance()) != 0) {
            return false;
        }
        return true;
    }

    public void addReservation(int nb) {
        if (nb <= getNbPlaceDispo()) {
            this.placeOccuper += nb;
        }
    }

    /**
     * retourne la distance parcourue
     * 
     * @return the distance
     */
    public double getDistance() {
        return distance;
    }

    /**
     * permet de changer la distance parcourue
     * 
     * @param distance
     *            the distance to set
     */
    public void setDistance(double distance) {
        this.distance = (int) distance;
    }
}
